package com.bright.ghj.overt.web.controller;

import com.bright.ghj.common.cache.TimeExpiredPoolCache;
import com.bright.ghj.common.pojo.bo.RestResult;
import com.bright.ghj.common.util.DateUtil;
import com.bright.ghj.common.util.ListUtil;
import com.bright.ghj.common.util.StringUtil;
import com.bright.ghj.overt.constant.DockingConstant;
import com.bright.ghj.overt.pojo.query.TableDataQuery;
import com.bright.ghj.overt.pojo.vo.TablePageVO;
import com.bright.ghj.overt.service.BaseDataService;
import com.bright.ghj.overt.util.DockingUtil;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.*;

/**
 * @Author hxj
 * @Date 2025/5/6 14:28
 * @Description 第三方数据对接
 */

@RestController
@ConditionalOnProperty(name = "docking.enable", havingValue = "true")
@RequiredArgsConstructor
@Slf4j
public class DockingController {

    private final BaseDataService baseDataService;

    private final DockingConstant dockingConstant;

    // 防重放攻击参数
    private static final String CACHE_KEY_PREFIX = "NONCE_";
    private static final long CACHE_VALID_TIME = 10 * 60 * 1000L;

    @GetMapping("/getPublicData")
    public RestResult getPublicData(@Valid DockingApiQuery query) {
        try {
            // 检查时间戳是否过期
            checkTimestampValid(query.getTimestamp());

            // 检查重放攻击 nonce十分钟内只能用一次
            checkNonce(query.getNonce());

            // 检查签名是否有效
            String appSecret = dockingConstant.getAppSecretByAppKey(query.getAppKey());
            checkSign(query, appSecret);

            // 检查请求是否合法 比如年月是否正常 数据是否已审核
            checkQuery(query);

            // 获取数据
            List<Map<String, Object>> data = getData(query);
            return RestResult.success(data);
        } catch (DockingException e) {
            return RestResult.fail(e.getMessage());
        }
    }

    private List<Map<String, Object>> getData(DockingApiQuery query) {
        TableDataQuery tableDataQuery = getTableDataQuery(query);
        TablePageVO<Map<String, Object>> mapTablePageVO = baseDataService.listTableData(tableDataQuery);
        List<Map<String, Object>> data = mapTablePageVO.getData();
        // 转换字段名称为小写
        List<Map<String, Object>> result = ListUtil.toSingleList(data, datum -> {
            Map<String, Object> map = new HashMap<>();
            for (Map.Entry<String, Object> entry : datum.entrySet()) {
                String lowerCaseKey = entry.getKey().toLowerCase();
                // 排除id列
                if (!StringUtil.equals(lowerCaseKey, "id")) {
                    map.put(lowerCaseKey, entry.getValue());
                }
            }
            return map;
        });
        return result;
    }

    private static TableDataQuery getTableDataQuery(DockingApiQuery query) {
        TableDataQuery tableDataQuery = new TableDataQuery();
        tableDataQuery.setYears(query.getYears());
        tableDataQuery.setMonths(query.getMonths());
        tableDataQuery.setZtId(query.getZth());

        String tableName = query.getTableName();
        // 处理tableName有特殊字符情形
        if (StringUtil.equalsIgnoreCase(tableName, "Rep_05")) {
            tableName = "Rep_05&6";
        }
        tableDataQuery.setTableName(tableName);
        return tableDataQuery;
    }

    private static void checkTimestampValid(String timestamp) {
//        try {
//            // URL解码
//            timestamp = URLDecoder.decode(timestamp, "UTF-8");
//        } catch (UnsupportedEncodingException e) {
//            throw new RuntimeException(e);
//        }

        Date timestampDate;
        try {
            // 校验时间戳是否过期 正负五分钟内有效
            timestampDate = DateUtil.parseStringFormat(timestamp, "yyyy-MM-dd HH:mm:ss");
        } catch (Exception e) {
            throw new DockingException("时间戳格式异常：" + timestamp);
        }
        boolean isValid = DockingUtil.checkTimestamp(timestampDate);
        if (!isValid) {
            throw new DockingException("时间戳已过期：" + timestamp);
        }
    }

    private static void checkNonce(String nonce) {
        TimeExpiredPoolCache cache = TimeExpiredPoolCache.getInstance();
        Object cacheVal = cache.get(CACHE_KEY_PREFIX + nonce);
        boolean isValid = cacheVal == null;
        if (isValid) {
            // 是未用过的随机字符串 放进缓存 十分钟有效（1是占位用）
            cache.put(CACHE_KEY_PREFIX + nonce, 1, CACHE_VALID_TIME);
        } else {
            throw new DockingException("nonce已使用：" + nonce);
        }
    }

    private void checkQuery(DockingApiQuery query) {
        // TODO 校验只能查上个月及之前的年月


        // TODO 是否要在baseDataService抽根据tableDataQuery校验是否已审核的方法
//        PageVO<OpenReportRecordVO> pageVO = baseDataService.listOpenReportRecordByPage(openQuery, PublicConstant.LIST_TYPE_EDIT);
        boolean isValid = true;
        if (!isValid) {
            throw new DockingException("该数据未审核：" + query);
        }
    }

    private static void checkSign(DockingApiQuery query, String appSecret) {
        if (StringUtil.isEmpty(appSecret)) {
            throw new DockingException("appKey有误！");
        }

        String nonce = query.getNonce();
        String timestamp = query.getTimestamp();
        String sign = query.getSign();

//        try {
//            // URL解码
//            sign = URLDecoder.decode(sign, "UTF-8");
//        } catch (UnsupportedEncodingException e) {
//            throw new RuntimeException(e);
//        }


        // 获取签名
        String realSignValue;
        try {
            realSignValue = DockingUtil.getSign(appSecret, nonce, timestamp);
        } catch (Exception e) {
            log.error("验签时发生异常：", e);
            throw new DockingException("验签时发生异常：" + e.getMessage());
        }

        // 验证签名
        if (!StringUtil.equals(realSignValue, sign)) {
            log.debug("realSignValue: {}, sign: {}", realSignValue, sign);
            throw new DockingException("验签失败，签名不合法！");
        }
    }


    @Data
    private class DockingApiQuery {
        // 查询参数
        @NotBlank
        String tableName;
        @NotNull
        Integer years;
        @NotNull
        Integer months;
        @NotBlank
        String zth;
        // 验签参数
        @NotBlank
        String appKey;
        @NotBlank
        String nonce;
        @NotBlank
        String timestamp;
        @NotBlank
        String sign;
    }

    private static class DockingException extends RuntimeException {
        public DockingException(String message) {
            super(message);
        }
    }

}
